/*
 * Copyright (c) 2011-2023, ThinkWide (cnet2001@163.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.thinkwide.data.mvc.action;

import com.thinkwide.data.ormapping.MFDbTypeTransfer;
import com.thinkwide.data.ormapping.MFReflect;
import com.thinkwide.data.core.MFPath;
import com.thinkwide.data.core.MFString;
import com.thinkwide.data.debug.MFDebug;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class MFActionManager {

    private List<String> includeFiles;
    private Map<String, String> configs;
    private MFActionItems items;
    private HttpServletRequest request;
    private HttpServletResponse response;
    private static String ext = ".tw";
    private static String execute = "execute";

    public MFActionManager() {
        includeFiles = new ArrayList<String>();
        configs = new HashMap<String, String>();
        //loadMain();
        items = new MFActionItems();
    }

    public MFActionManager(HttpServletRequest request, HttpServletResponse response) {
        this();
        updateState(request, response);
    }

    public void updateState(HttpServletRequest request, HttpServletResponse response) {
        this.request = request;
        this.response = response;
    }

    public boolean prepareProperty(Class<?> classType, Object obj) throws Throwable {
        if (obj instanceof MFRequestAware) {
            ((MFRequestAware) obj).setRequest(request);
            ((MFRequestAware) obj).setResponse(response);
        }

        Enumeration params = request.getParameterNames();
        while (params.hasMoreElements()) {
            String name = (String) params.nextElement();
            String value = request.getParameter(name);

            if (MFString.notNullOrEmpty(name) && MFReflect.isHaveProperty(obj, name)) {
                Class<?> type = MFReflect.getPropertyType(obj, name);
                Object typedValue = MFDbTypeTransfer.toJavaType(type, value);
                MFReflect.setProperty(obj, name, typedValue);

            }
        }
        return true;
    }

    public boolean runAction(MFActionItem item) throws Throwable {
        if (item != null) {
            Class<?> c = Class.forName(item.getClassName());

            Object obj = c.newInstance();

            prepareProperty(c, obj);

            String methodName = MFString.nullToString(item.getMethod(), execute);
            Method method = c.getDeclaredMethod(methodName, null);

            Object value = method.invoke(obj, null);
            if (value != null) {
                String url = item.getResults().get(value);
                if (MFString.notNullOrEmpty(url)) {
                    //String path=MFString.nullToString(item.getNamespace())+url;
                    response.sendRedirect(url);
                }
            }

        }
        return false;
    }

    public boolean runAction(String url) throws Throwable {
        MFActionItem item = findAction(url);
        return runAction(item);
    }

    public String CheckPath(String url) {
        if (MFString.notNullOrEmpty(url) && !url.startsWith(MFPath.useparator)) {
            url = MFPath.useparator + url;
        }
        return url;
    }

    public MFActionItem findAction(String url) {
        for (MFActionItem item : items) {
            String Str = MFString.nullToString(item.getNamespace()) + item.getActionName() + ext;
            Str = CheckPath(Str);

            if (MFString.compareNoCase(url, Str)) {
                MFDebug.println("find");
                return item;
            }

        }

        return null;
    }

    public String getAttrValue(Element item, String name) {
        Attribute attr = item.attribute(name);
        if (attr != null) {
            String text = attr.getText();
            return text;
        }
        return null;
    }

    public boolean loadConfig(String filePath) throws DocumentException {
        File file = new File(filePath);

        SAXReader reader = new SAXReader();
        Document doc = reader.read(file);
        Element root = doc.getRootElement();

        //load include file
        for (Iterator<?> i = root.elementIterator("include"); i.hasNext(); ) {
            Element item = (Element) i.next();
            String value = getAttrValue(item, "file");
            if (MFString.notNullOrEmpty(value)) {
                includeFiles.add(value);
            }
        }

        //load constant
        for (Iterator<?> i = root.elementIterator("constant"); i.hasNext(); ) {
            Element item = (Element) i.next();

            String conName = getAttrValue(item, "name");
            String conValue = getAttrValue(item, "value");
            if (MFString.notNullOrEmpty(conName) && MFString.notNullOrEmpty(conValue)) {
                configs.put(conName, conValue);
            }
        }

        //load action
        for (Iterator<?> i = root.elementIterator("package"); i.hasNext(); ) {
            Element packageItem = (Element) i.next();
            String packageName = getAttrValue(packageItem, "name");
            String namespace = getAttrValue(packageItem, "namespace");

            for (Iterator<?> j = packageItem.elementIterator("action"); j.hasNext(); ) {
                MFActionItem action = new MFActionItem();
                Element actionItem = (Element) j.next();

                String actionName = getAttrValue(actionItem, "name");
                String className = getAttrValue(actionItem, "class");
                String method = getAttrValue(actionItem, "method");

                action.setPackageName(packageName);
                action.setNamespace(namespace);
                action.setActionName(actionName);
                action.setActionName(actionName);
                action.setClassName(className);
                action.setMethod(method);

                for (Iterator<?> k = actionItem.elementIterator("result"); k.hasNext(); ) {
                    Element resultItem = (Element) k.next();

                    String resultName = getAttrValue(resultItem, "name");
                    String url = (String) resultItem.getData();
                    action.getResults().put(resultName, url);
                }

                items.add(action);
            }
        }

        return true;
    }

    public void setIncludeFiles(List<String> includeFiles) {
        this.includeFiles = includeFiles;
    }

    public List<String> getIncludeFiles() {
        return includeFiles;
    }
}
